package cn.cloudtogo.common.types;

import cn.cloudtogo.common.Assert;
import cn.cloudtogo.common.GendbException;
import cn.cloudtogo.common.model.DatasourceTypes;
import lombok.Data;
import org.springframework.util.StringUtils;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;

import static cn.cloudtogo.common.util.StringHelper.parseParam;

/**
 * @author yaolianhua789@gmail.com
 **/
@Data
public class ConnectString {

    public final static String SCHEME_JDBC_MYSQL = "jdbc:mysql://";
    public final static String SCHEME_MYSQL = "mysql://";
    public final static String SCHEME_MONGODB = "mongodb://";
    public final static String MONGODB = "mongodb";
    public final static String MYSQL = "mysql";
    private final Credential credential;
    private final Endpoint endpoint;
    private final String schema;
    private DatasourceTypes type;

    public ConnectString(DatasourceTypes type,
                         Credential credential,
                         Endpoint endpoint,
                         String schema) {
        Assert.argument(credential != null, () -> "userInfo is null");
        Assert.argument(endpoint != null, () -> "serverInfo is null");
        Assert.argument(endpoint.getHost() != null && !endpoint.getHost().isEmpty(), () -> "host is null");
        Assert.argument(endpoint.getPort() != null, () -> "port is null");
        Assert.argument(schema != null && !schema.isEmpty(), () -> "schema is null");

        this.type = type;
        this.credential = credential;
        this.endpoint = endpoint;
        this.schema = schema;
    }

    public static String constructUrl(ConnectString connectString) {

        DatasourceTypes type = connectString.getType();
        Credential credential = connectString.getCredential();
        Endpoint endpoint = connectString.getEndpoint();

        String schema = connectString.getSchema();

        Assert.notNull(connectString, () -> "connectString is null");
        Assert.argument(type != null, () -> "datasource type is null");
        Assert.argument(credential != null, () -> "userInfo is null");
        Assert.argument(endpoint != null, () -> "serverInfo is null");
        Assert.argument(endpoint.getHost() != null && !endpoint.getHost().isEmpty(), () -> "host is null");
        Assert.argument(endpoint.getPort() != null, () -> "port is null");

        Assert.argument(schema != null && !schema.isEmpty(), () -> "schema is null");

        switch (type) {
            case Mysql:
                return String.format("jdbc:mysql://%s:%s/%s?user=%s&password=%s", endpoint.getHost(), endpoint.getPort(), schema, credential.getUser(), credential.getPassword());
            case Mongo:
                return String.format("mongodb://%s:%s@%s:%s/%s", credential.getUser(), credential.getPassword(), endpoint.getHost(), endpoint.getPort(), schema);
            default:
                throw new GendbException(String.format("Unsupported type '%s'", type));
        }

    }

    public static ConnectString getConnectString(String url) {
        return parse(url);
    }

    private static ConnectString parse(String url) {
        Assert.argument(url != null && !url.isEmpty(), () -> "url is null");
        DatasourceTypes type;
        if (url.startsWith(SCHEME_JDBC_MYSQL)) {
            url = url.substring(5);
            type = DatasourceTypes.Mysql;
        } else if (url.startsWith(SCHEME_MYSQL)) {
            type = DatasourceTypes.Mysql;
        } else if (url.startsWith(SCHEME_MONGODB)) {
            type = DatasourceTypes.Mongo;
        } else {
            throw new GendbException(String.format("Unsupported url '%s'", url), 400);
        }
        String username = null;
        String password = null;
        try {
            URI uri = new URI(url);

            String userInfo = uri.getUserInfo();
            if (userInfo != null && !userInfo.isEmpty()) {
                String candidate = userInfo;
                int index = candidate.indexOf(':');
                if (index >= 0) {
                    username = candidate.substring(0, index);
                    password = candidate.substring(index + 1);
                } else {
                    password = candidate;
                }
            }

            String uriPath = uri.getPath();
            String schema = null;
            if (StringUtils.hasText(uriPath)) {
                schema = uriPath.substring(1);
            }

            if (!StringUtils.hasText(username) && !StringUtils.hasText(password)) {
                Map<String, String> param = parseParam(url);
                username = param.get("user");
                password = param.get("password");
            }

            return new ConnectString(type, Credential.of(username, password), Endpoint.of(uri.getHost(), uri.getPort()), schema);
        } catch (URISyntaxException ex) {
            throw new GendbException(String.format("Parse url error. [%s]", ex.getMessage()), 400);
        }
    }

}
